﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using HK.Core.Common.Data;
using HK.Core.Extend;
using HK.Core.Pool.Protocol;
using UnityEngine;
using Logger = HK.Core.Logs.Logger;

namespace HK.Core.Pool
{
    
    /// <summary>
    /// 缓存池
    /// <para>* 缓存池中的对象若使用完毕，需要还回缓存池</para>
    /// <para>* 预先生成缓存池对象，缓存池的数据不能保证可信任，仅仅只是预先分配内存用。只有Pop出去的元素才在重置或者填充过数据后，才可信任</para>
    /// </summary>
    /// <typeparam name="TElement">缓存池对象类型</typeparam>
    public static class ObjectPool<TElement> where TElement : DataBase, IPoolElement, new()
    {

        /// <summary>
        /// 初始化标志位
        /// </summary>
        public static bool Initialized { get; private set; } = false;

        /// <summary>
        /// 默认容量
        /// </summary>
        private static readonly int DefaultCapacity = 999;
        /// <summary>
        /// 缓存池容量
        /// <para>* 默认容量 : 10</para>
        /// </summary>
        public static int Capacity { get; private set; } = DefaultCapacity;
        
        /// <summary>
        /// 自动补足
        /// <para>* 默认容量 : 10</para>
        /// </summary>
        public static bool AutoFill { get; private set; } = false;

        /// <summary>
        /// 警戒线
        /// <para>* 警戒线</para>
        /// <para>* 当缓存池可以自动补足的场合，每当当前容量低于警戒线时，会自动补足一个成员.(默认:满容量自动补足)</para>
        /// </summary>
        public static int WarningLine { get; private set; } = 10;

        /// <summary>
        /// 允许移除标志位
        /// <para>* 当前容量已满的情况下，是否允许容量溢出，继续追加</para>
        /// <para>* 虽然可以允许移除，但是溢出场合下，还原的元素将被直接抛弃，此时缓冲池队列也只允许只出不进，知道有闲余空间产生，才会接受归还元素返回缓冲池队列</para>
        /// </summary>
        public static bool AllowOverflow { get; private set; } = false;

        /// <summary>
        /// 溢出标志位
        /// </summary>
        public static bool Overflow => Capacity < _elements.Count;

        /// <summary>
        /// 缓存池是否已经满了
        /// </summary>
        public static bool Full => Capacity <= _elements.Count;
        
        /// <summary>
        /// 缓存池队列
        /// </summary>
        private static Queue<TElement> _elements = new Queue<TElement>();
        
        /// <summary>
        /// 生存缓存池
        /// <para>* 缓存池容量溢出时，若允许溢出则只有外部往缓存池中手动Push或者还原时才会让缓存池继续增加</para>
        /// <para>* 自动补足的场合，会在当前缓存池容量低于警戒线数量时，会在每次Pop出去后，自动补足一个缓存池对象</para>
        /// </summary>
        /// <param name="iCapacity">缓存池容量</param>
        /// <param name="iAutoFill">自动补足</param>
        /// <param name="iWarningLine">警戒线(0.0f~1.0f)</param>
        /// <param name="iAllowOverflow">允许溢出</param>
        public static void Generate(int iCapacity = -1, bool iAutoFill = false, float iWarningLine = 1.0f, bool iAllowOverflow = false)
        {
            // 已初始化
            if(Initialized) return;

            // 设置容量
            var curAddCapacity = iCapacity;
            if (-1 == iCapacity)
            {
                curAddCapacity = DefaultCapacity;
            }
            Capacity = curAddCapacity;   
            AutoFill = iAutoFill;
            
            // 警戒线
            var warningLine = Mathf.Clamp(iWarningLine, 0.1f, 1.0f);
            WarningLine = Mathf.CeilToInt(Capacity * warningLine);
            AllowOverflow = iAllowOverflow;

            // 自动补足缓存池
            Initialized = AutoFillPool(curAddCapacity);
        }
        
        /// <summary>
        /// 自动补足缓存池
        /// </summary>
        /// <param name="iDstCapacity">目标容量</param>
        /// <returns>true:OK; false:NG;</returns>
        private static bool AutoFillPool(int iDstCapacity)
        {
            var restCount = Math.Max(0, iDstCapacity - _elements.Count);
            if (0 >= restCount) return false;
            
            for (var idx = 0; idx < restCount; ++idx)
            {
                // 不允许溢出，且已满，则返回
                if(!AllowOverflow && Full) break;
                
                _elements.Enqueue(GenerateElement());
            }

            return true;
        }
        
        /// <summary>
        /// 自动补足缓存池容量到警戒线
        /// </summary>
        private static void AutoFillPoolByWarningLine()
        {
            // 不用自动补足的场合
            if(!AutoFill) return;
            
            var curCount = _elements.Count;
            if(curCount >= WarningLine) return;
            
            // 自动补足的场合，当前容量低于警戒线时，不足成员
            AutoFillPool(WarningLine);
        }

        /// <summary>
        /// 生成一个Element
        /// </summary>
        /// <returns>Element</returns>
        private static TElement GenerateElement() => new TElement();
        private static long _guidCount = 0L;
        
        /// <summary>
        /// Pop
        /// </summary>
        /// <param name="iOnPopped">Pop回调事件</param>
        /// <returns>缓存池元素</returns>
        public static TElement Pop(Action<TElement> iOnPopped = null)
        {   
            // 尚未初始化
            if (!Initialized)
            {
                Logger.Error($"ObjectPool::Pop():The pool had not been initialized!({typeof(TElement).Name})");
                return default(TElement);
            }
            
            var curCount = _elements.Count;
            if (0 >= curCount)
            {
                if (AutoFill)
                {
                    Logger.Warning($"ObjectPool::Pop():There is no element({typeof(TElement).Name} " +
                                   $"Capacity:{_elements.Count}/{Capacity}) left in the pool!");
                }
                else
                {
                    Logger.Error($"ObjectPool::Pop():There is no element({typeof(TElement).Name} " +
                                 $"Capacity:{_elements.Count}/{Capacity}) left in the pool!");
                }
                return GenerateElement();
            }

            var popTarget = _elements.Dequeue() ?? GenerateElement();
            if (default(TElement) == popTarget) return default(TElement);

            // 清空
            popTarget.Clear();
            iOnPopped?.Invoke(popTarget);
            
            // 自动补足的场合，当前容量低于警戒线时，不足成员
            AutoFillPoolByWarningLine();

            // 标注缓存池Id
            if (-1 == popTarget.PoolId)
            {
                popTarget.PoolId = _guidCount;
                ++_guidCount;
            }
            
            return popTarget;
        }
        
        /// <summary>
        /// 回收对象
        /// <para>* 超过缓存池容量限制，会被丢弃，除非允许溢出</para>
        /// </summary>
        /// <param name="iElement">缓存池Element</param>
        /// <returns>true:归还成功; false:归还失败;</returns>
        public static bool Recycle(TElement iElement)
        {
            if (default(TElement) == iElement) return false;
            
            // 尚未初始化
            if (!Initialized)
            {
                Logger.Error($"ObjectPool::Recycle():The pool had not been initialized!({typeof(TElement).Name})");
                return false;
            }
            
            // 不允许溢出，则若已满，则返回
            if (!AllowOverflow && Full)
            {
                Logger.Error($"ObjectPool::Recycle():The pool queue({typeof(TElement).Name}) had been overflow!" +
                             $"(Capacity:{_elements.Count}/{Capacity} AllowOverflow:{AllowOverflow})");
                return false;
            }
            
            iElement.Clear();
            _elements.Enqueue(iElement);
            return true;
        }

        /// <summary>
        /// 清空缓存池队列
        /// </summary>
        public static void Clear()
        {
            _elements.Clear();
        }
    }
    
    /// <summary>
    /// 缓存池
    /// <para>* 线程安全缓存池</para>
    /// <para>* 缓存池中的对象若使用完毕，需要还回缓存池</para>
    /// </summary>
    /// <typeparam name="TElement">缓存池对象类型</typeparam>
    public class ThreadPool<TElement> where TElement : DataBase, IPoolElement, new()
    {

        /// <summary>
        /// 初始化标志位
        /// </summary>
        public static bool Initialized { get; private set; } = false;

        /// <summary>
        /// 默认容量
        /// </summary>
        private static readonly int DefaultCapacity = 10;

        /// <summary>
        /// 缓存池容量
        /// <para>* 默认容量 : 10</para>
        /// </summary>
        public static int Capacity { get; private set; } = DefaultCapacity;
        
        /// <summary>
        /// 自动补足
        /// <para>* 默认容量 : 10</para>
        /// </summary>
        public static bool AutoFill { get; private set; } = false;

        /// <summary>
        /// 警戒线
        /// <para>* 警戒线</para>
        /// <para>* 当缓存池可以自动补足的场合，每当当前容量低于警戒线时，会自动补足一个成员.(默认:满容量自动补足)</para>
        /// </summary>
        public static int WarningLine { get; private set; } = 10;

        /// <summary>
        /// 允许移除标志位
        /// <para>* 当前容量已满的情况下，是否允许容量溢出，继续追加</para>
        /// <para>* 虽然可以允许移除，但是溢出场合下，还原的元素将被直接抛弃，此时缓冲池队列也只允许只出不进，知道有闲余空间产生，才会接受归还元素返回缓冲池队列</para>
        /// </summary>
        public static bool AllowOverflow { get; private set; } = false;

        /// <summary>
        /// 溢出标志位
        /// </summary>
        public static bool Overflow => Capacity < _elements.Count; 

        /// <summary>
        /// 缓存池是否已经满了
        /// </summary>
        public static bool Full => Capacity <= _elements.Count;
        
        /// <summary>
        /// 缓存池队列
        /// </summary>
        private static ConcurrentQueue<TElement> _elements = new ConcurrentQueue<TElement>();
        
        /// <summary>
        /// 生存缓存池
        /// <para>* 缓存池容量溢出时，若允许溢出则只有外部往缓存池中手动Push或者还原时才会让缓存池继续增加</para>
        /// <para>* 自动补足的场合，会在当前缓存池容量低于警戒线数量时，会在每次Pop出去后，自动补足一个缓存池对象</para>
        /// </summary>
        /// <param name="iCapacity">缓存池容量</param>
        /// <param name="iAutoFill">自动补足</param>
        /// <param name="iWarningLine">警戒线(0.0f~1.0f)</param>
        /// <param name="iAllowOverflow">允许溢出</param>
        public static void Generate(int iCapacity = -1, bool iAutoFill = false, float iWarningLine = 1.0f, bool iAllowOverflow = false)
        {
            // 已初始化
            if(Initialized) return;
            
            // 设置容量
            var curAddCapacity = iCapacity;
            if (-1 != iCapacity)
            {
                curAddCapacity = DefaultCapacity;
            }
            Capacity = curAddCapacity;
            AutoFill = iAutoFill;
                
            // 警戒线
            var warningLine = Mathf.Clamp(iWarningLine, 0.1f, 1.0f);
            WarningLine = Mathf.CeilToInt(Capacity * warningLine);
            AllowOverflow = iAllowOverflow;

            // 补足缓存池
            Initialized = AutoFillPool(curAddCapacity);
        }

        /// <summary>
        /// 自动补足缓存池
        /// </summary>
        /// <param name="iDstCapacity">目标容量</param>
        /// <returns>true:OK; false:NG;</returns>
        private static bool AutoFillPool(int iDstCapacity)
        {
            var restCount = Math.Max(0, iDstCapacity - _elements.Count);
            if (0 >= restCount) return false;
            
            for (var idx = 0; idx < restCount; ++idx)
            {
                // 不允许溢出，且已满，则返回
                if(!AllowOverflow && Full) break;
                
                _elements.Enqueue(GenerateElement());
            }

            return true;
        }
        
        /// <summary>
        /// 自动补足缓存池容量到警戒线
        /// </summary>
        private static void AutoFillPoolByWarningLine()
        {
            // 不用自动补足的场合
            if(!AutoFill) return;
            
            var curCount = _elements.Count;
            if(curCount >= WarningLine) return;
            
            // 自动补足的场合，当前容量低于警戒线时，不足成员
            AutoFillPool(WarningLine);
        }

        /// <summary>
        /// 生成一个Element
        /// </summary>
        /// <returns>Element</returns>
        private static TElement GenerateElement() => new TElement();
        private static long _guidCount = 0L;

        /// <summary>
        /// Pop
        /// </summary>
        /// <param name="iOnPopped">Pop回调事件</param>
        /// <returns>缓存池元素</returns>
        public static TElement Pop(Action<TElement> iOnPopped = null)
        {
            var curCount = _elements.Count;
            if (0 >= curCount)
            {
                if (AutoFill)
                {
                    Logger.Warning($"ThreadPool::Pop():There is no element({typeof(TElement).Name} " +
                                   $"Capacity:{_elements.Count}/{Capacity}) left in the pool!");
                }
                else
                {
                    Logger.Error($"ThreadPool::Pop():There is no element({typeof(TElement).Name} " +
                                 $"Capacity:{_elements.Count}/{Capacity}) left in the pool!");
                }
                return new TElement();
            }
            
            var popTarget = _elements.TryDequeue(out var oElement) ? oElement : default(TElement);
            if (default(TElement) == popTarget) return default(TElement);

            // 清空
            popTarget.Clear();
            iOnPopped?.Invoke(popTarget);
            
            // 自动补足的场合，当前容量低于警戒线时，不足成员
            AutoFillPoolByWarningLine();

            // 标注缓存池Id
            if (-1 == popTarget.PoolId)
            {
                popTarget.PoolId = _guidCount;
                ++_guidCount;
            }
            
            return popTarget;
        }

        /// <summary>
        /// 回收对象
        /// <para>* 超过缓存池容量限制，会被丢弃，除非允许溢出</para>
        /// </summary>
        /// <param name="iElement">缓存池Element</param>
        /// <returns>true:归还成功; false:归还失败;</returns>
        public static bool Recycle(TElement iElement)
        {   
            if (default(TElement) == iElement) return false;
            // 尚未初始化
            if (!Initialized) return false;
            
            // 不允许溢出，则若已满，则返回
            if (!AllowOverflow && Full)
            {
                Logger.Error($"ThreadPool::Recycle():The pool({typeof(TElement).Name}) queue had been overflow!" +
                             $"(Capacity:{_elements.Count}/{Capacity} AllowOverflow:{AllowOverflow})");
                return false;
            }
            
            iElement.Clear();
            _elements.Enqueue(iElement);
            return true;
        }

        /// <summary>
        /// 清空缓存池队列
        /// </summary>
        public static void Clear()
        {
            _elements.Clear();
        }
    }
}
